package message.utils;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

public class XmlUtils {
    private static final String ATTRIBUTE = ".attribute";
    public static final String KEY_ENCODING = "encoding";
    public static final String KEY_ROOT_NAME = "rootName";

    public XmlUtils() {
    }

    public static Map<String, Object> parseXmlToData(String xml, Map<String, String> out) throws DocumentException {
        Document doc = parseXml(xml);
        if (doc == null) {
            throw new DocumentException("解析xml出错。");
        } else {
            String encoding = doc.getXMLEncoding();
            Element root = doc.getRootElement();
            out.put("encoding", encoding);
            out.put("rootName", root.getName());
            Map<String, Object> result = getElementDataByPath(doc, root.getName());
            return result;
        }
    }

    public static Map<String, Object> parseXmlToData(String xml) throws DocumentException {
        Document doc = parseXml(xml);
        if (doc == null) {
            throw new DocumentException("解析xml出错。");
        } else {
            Element root = doc.getRootElement();
            Map<String, Object> result = getElementDataByPath(doc, root.getName());
            return result;
        }
    }

    public static String buildXml(Map<String, Object> data, String charset) throws Exception {
        Map<String, Object> xmlData = null;

        Entry entry;
        for (Iterator var4 = data.entrySet().iterator(); var4.hasNext(); xmlData = buildOneRecord(xmlData, (String) entry.getKey(), entry.getValue() == null ? "" : entry.getValue())) {
            entry = (Entry) var4.next();
        }

        return parseDataToXml(xmlData, charset);
    }

    public static String parseDataToXml(Map<String, Object> xmlData, String charset) throws Exception {
        String root = null;
        Map<String, Object> childrenData = null;
        if (xmlData == null) {
            return null;
        } else {
            Iterator var5 = xmlData.entrySet().iterator();

            while (var5.hasNext()) {
                Entry<String, Object> entry = (Entry) var5.next();
                String key = (String) entry.getKey();
                if (!key.equals(".attribute")) {
                    root = key;
                    childrenData = (Map) entry.getValue();
                    break;
                }
            }

            Document doc = DocumentHelper.createDocument();
            doc.setXMLEncoding(charset);
            Element rootEl = doc.addElement(root);
            if (rootEl != null && childrenData != null) {
                addElements(rootEl, childrenData);
                return doc.asXML();
            } else {
                throw new NullPointerException();
            }
        }
    }

    public static Object getNodeDataByPath(String path, Map<String, Object> data) {
        String[] arr = path.split("/");
        String key = arr[arr.length - 1];

        for (int i = 0; i < arr.length - 1; ++i) {
            if (data == null || !(data.get(arr[i]) instanceof Map)) {
                return null;
            }

            data = (Map) data.get(arr[i]);
        }

        return data.get(key);
    }

    public static List<Map<String, Object>> getLoopDataByPath(String path, Map<String, Object> data) {
        Object obj = getNodeDataByPath(path, data);
        if (obj == null) {
            return null;
        } else {
            List<Map<String, Object>> result = null;
            if (obj instanceof List) {
                result = (List) obj;
            } else {
                result = new ArrayList();
                ((List) result).add((Map) obj);
            }

            return (List) result;
        }
    }

    private static Map<String, Object> buildOneRecord(Map<String, Object> xmlData, String path, Object value) throws Exception {
        String[] arr = path.split("/");
        String key = arr[arr.length - 1];
        if (xmlData == null) {
            xmlData = new LinkedHashMap();
        }

        Map<String, Object> temp = xmlData;

        for (int i = 0; i < arr.length - 1; ++i) {
            if (((Map) temp).get(arr[i]) == null) {
                ((Map) temp).put(arr[i], new LinkedHashMap());
            }

            temp = (Map) ((Map) temp).get(arr[i]);
        }

        ((Map) temp).put(key, value);
        return (Map) xmlData;
    }

    private static void addElements(Element parent, Map<String, Object> dataMap) {
        Iterator var3 = dataMap.entrySet().iterator();

        while (true) {
            while (true) {
                Entry entry;
                String key;
                do {
                    if (!var3.hasNext()) {
                        return;
                    }

                    entry = (Entry) var3.next();
                    key = (String) entry.getKey();
                } while (key.contains(".attribute"));

                Object value = entry.getValue();
                Element child;
                if (value instanceof String) {
                    child = parent.addElement(key);
                    child.setText(value.toString());
                    if (dataMap.containsKey(key + ".attribute")) {
                        setAttr(child, (Map) dataMap.get(key + ".attribute"));
                    }
                } else if (value instanceof Map) {
                    child = parent.addElement(key);
                    addElements(child, (Map) value);
                    if (dataMap.containsKey(key + ".attribute")) {
                        setAttr(child, (Map) dataMap.get(key + ".attribute"));
                    }
                } else {
                    List<Map<String, Object>> childList = (List) value;
                    Iterator var8 = childList.iterator();

                    while (var8.hasNext()) {
                        Map<String, Object> childData = (Map) var8.next();
                        Element childEl = parent.addElement(key);
                        addElements(childEl, childData);
                    }
                }
            }
        }
    }

    private static void setAttr(Element e, Map<String, String> map) {
        if (map != null && !map.isEmpty()) {
            Set<String> set = map.keySet();
            Iterator it = set.iterator();

            while (it.hasNext()) {
                String s = (String) it.next();
                if (s != null && !s.equals("")) {
                    e.addAttribute(s, (String) map.get(s));
                }
            }
        }

    }

    private static Map<String, Object> getElementDataByPath(Document doc, String path) throws NullPointerException {
        Element el = findElementByPath(doc, path);
        return getElementData(el);
    }

    private static Element findElementByPath(Document doc, String path) throws NullPointerException {
        String[] paths = path.split("/");
        Element root = doc.getRootElement();
        if (!root.getName().equals(paths[0])) {
            throw new NullPointerException(path + "的" + paths[0] + "节点未找到");
        } else {
            Element el = root;

            for (int i = 1; i < paths.length; ++i) {
                String tempPath = paths[i];
                if (tempPath.contains("[")) {
                    int start = tempPath.lastIndexOf("[");
                    int end = tempPath.lastIndexOf("]");
                    Integer index = Integer.parseInt(tempPath.substring(start + 1, end));
                    List<Element> tempList = el.elements(tempPath.substring(0, start));
                    el = (Element) tempList.get(index);
                } else {
                    el = el.element(tempPath);
                }

                if (el == null) {
                    throw new NullPointerException(path + "的" + tempPath + "节点未找到");
                }
            }

            return el;
        }
    }

    private static Document parseXml(String xml) throws DocumentException {
        Document doc = DocumentHelper.parseText(xml.trim());
        return doc;
    }

    private static Map<String, Object> getElementData(Element el) {
        Map<String, Object> result = new LinkedHashMap();
        if (el.elements().size() > 0) {
            result.put(el.getName(), getChildrenData(el));
        } else {
            result.put(el.getName(), el.getText());
        }

        if (el.attributes() != null && el.attributes().size() > 0) {
            result.put(el.getName() + ".attribute", parseAttributes(el.attributes()));
        }

        return result;
    }

    private static Map<String, Object> getChildrenData(Element el) {
        Map<String, Object> childrenData = new LinkedHashMap();
        List<Element> children = el.elements();
        Iterator var4 = children.iterator();

        while (var4.hasNext()) {
            Element child = (Element) var4.next();
            String name = child.getName();
            if (child.attributes().size() > 0) {
                childrenData.put(name + ".attribute", parseAttributes(child.attributes()));
            }

            Object obj = childrenData.get(name);
            if (obj == null) {
                if (child.elements().size() > 0) {
                    childrenData.put(name, getChildrenData(child));
                } else {
                    childrenData.put(name, child.getText());
                }
            } else if (obj != null) {
                if (!(obj instanceof List)) {
                    List<Map<String, Object>> list = new ArrayList();
                    list.add((Map) obj);
                    list.add(getChildrenData(child));
                    childrenData.put(name, list);
                } else {
                    ((List) obj).add(getChildrenData(child));
                }
            }
        }

        return childrenData;
    }

    private static Map<String, String> parseAttributes(List<Attribute> list) {
        Map<String, String> ats = new HashMap();

        for (int i = 0; i < list.size(); ++i) {
            ats.put(((Attribute) list.get(i)).getName(), ((Attribute) list.get(i)).getText());
        }

        return ats;
    }

    /**
     * 通过Map创建XML,Map可以多层转换
     * 可以自定义parent节点
     *
     * @param params
     * @return String-->XML
     */
    public static String createXmlByMap(String parentName, Map<String, Object> params) {
        return createXmlByMap(parentName, params, "UTF-8");
    }

    /**
     * 通过Map创建XML,Map可以多层转换
     * 可以自定义parent节点
     *
     * @param params
     * @return String-->XML
     */
    public static String createXmlByMap(String parentName, Map<String, Object> params, String encoding) {
        Document doc = DocumentHelper.createDocument();
        doc.setXMLEncoding(encoding);
        doc.addElement(parentName);
        String xml = iteratorXml(doc.getRootElement(), parentName, params, false);
        System.out.println("====="+xml);
        return formatXML(xml,encoding);
    }

    /**
     * MapToXml循环遍历创建xml节点
     * 此方法在value中加入CDATA标识符
     *
     * @param element    根节点
     * @param parentName 子节点名字
     * @param params     map数据
     * @return String-->Xml
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static String iteratorXml(Element element, String parentName, Map<String, Object> params, boolean isCDATA) {
        Element e = element.addElement(parentName);
        Set<String> set = params.keySet();
        for (Iterator<String> it = set.iterator(); it.hasNext(); ) {
            String key = (String) it.next();
            // 处理节点属性
            if (params.get(key) instanceof Map && ((Map) params.get(key)).containsKey("value")
                    && ((Map) params.get(key)).containsKey("attributes")) {
                Map map = (Map) params.get(key);
                String value = map.get("value") == null ? "" : map.get("value").toString();
                Map<String, String> attrMap = (Map) map.get("attributes");
                Element addElement = e.addElement(key);
                for (Map.Entry<String, String> entry : attrMap.entrySet()) {
                    System.out.println("设置节点属性：" + "name: " + entry.getKey() + " value: " + entry.getValue());
                    addElement.addAttribute(entry.getKey(), entry.getValue());
                }
                if (!isCDATA) {
                    addElement.addText(value);
                } else {
                    addElement.addElement(key).addCDATA(value);
                }
            } else if (params.get(key) instanceof Map) {
                iteratorXml(e, key, (Map<String, Object>) params.get(key), isCDATA);
            } else if (params.get(key) instanceof List) {
                List list = (List) params.get(key);
                for (Object obj : list) {
                    //未做，obj是否为 map 的校验
                    iteratorXml(e, key, (Map<String, Object>) obj, isCDATA);
                }
            } else {
                String value = params.get(key) == null ? "" : params.get(key).toString();
                if (!isCDATA) {
                    e.addElement(key).addText(value);
                } else {
                    e.addElement(key).addCDATA(value);
                }
            }
        }
        return e.asXML();
    }

    /**
     * 格式化xml,显示为容易看的XML格式
     *
     * @param inputXML
     * @return
     */
    public static String formatXML(String inputXML, String encoding) {
        String requestXML = null;
        XMLWriter writer = null;
        Document document = null;
        try {
            SAXReader reader = new SAXReader();
            document = reader.read(new StringReader(inputXML));
            if (document != null) {
                StringWriter stringWriter = new StringWriter();
                OutputFormat format = new OutputFormat("	", true);//格式化，每一级前的空格
                format.setNewLineAfterDeclaration(false);    //xml声明与内容是否添加空行
                format.setSuppressDeclaration(false);        //是否设置xml声明头部
                format.setEncoding(encoding);
                //format.setNewlines(true);		//设置分行
                writer = new XMLWriter(stringWriter, format);
                writer.write(document);
                writer.flush();
                requestXML = stringWriter.getBuffer().toString();
            }
//			System.out.println("格式化后XML报文："+ requestXML);
            return requestXML;
        } catch (Exception e1) {
            e1.printStackTrace();
            return null;
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {

                }
            }
        }
    }


}
